/*
 * kernel/fork.c
 *
 * last modified by ChenJie at 2007.07.22
 *
 */

#include <clinux/kernel.h>
#include <clinux/sched.h>
#include <clinux/mm.h>
#include <asm/i386.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>

/*
 * 功能：
 * 	为新的进程获取任务号和进程号
 * 输入:
 * 	无
 * 输出:
 * 	可被新进程使用的任务号
 * 全局变量:
 * 	last_pid	RW 当前进程中最大的进程号
 * 	task		R
 * 处理:
 * 	1.遍历任务数组, 寻找最大的进程号--->last_pid
 * 	2.遍厣任务数组, 寻找空闲的任务号--->返回, 如无返回 -EAGAIN
 * 	
 */
int find_empty_process(void)
{
	int i;
	for(i=1; i<NR_TASKS; i++){
		if(task[i] && task[i]->pid > last_pid){
			last_pid = task[i]->pid;
		}
	}
	for(i=1; i<NR_TASKS; i++){
		if(task[i]==NULL) return i;
	}
	return -EAGAIN;
}


/*
 *
 */
int copy_process(int nr, long ebx, long ecx, long edx, long edi, long esi, 
		long ebp, long ds, long es, long fs, long gs, 
		long eip, long cs, long eflags, 
		long esp, long ss)
{
	struct task_struct *p;
	unsigned long * new_pg_dir;

	new_pg_dir = 
		create_pg_dir_from(((unsigned long *)current_task->tss.cr3));
	if(new_pg_dir==0) return -EAGAIN;

	p = (struct task_struct *)mm_alloc_page();
	if(!p)	return -EAGAIN;

	*p = *current_task;

	p->state = TASK_UNINTERRUPTIBLE;
	p->counter = p->priority;
	p->priority = p->priority;

	p->exit_code = p->exit_code;

	p->pid = ++last_pid;
	p->father = current_task->pid;

	//p->signal = 0;
	//p->alarm = 0;
	//p->leader = 0;
	p->utime = p->stime = 0;
	p->cutime = p->cstime = 0;

	p->start_time = jiffies;

	p->used_math = current_task->used_math;

	// p->ldt = current_task->ldt;

	p->tss.back_link = 0;
	p->tss.esp0 = MM_PAGE_SIZE+(unsigned long)p;
	p->tss.ss0 = KERNEL_SS;
	p->tss.esp1 = 0;
	p->tss.ss1 = 0;
	p->tss.esp2 = 0;
	p->tss.ss2 = 0;
	p->tss.cr3 = (unsigned long)new_pg_dir;
	p->tss.eip = eip;
	p->tss.eflags = eflags;
	p->tss.eax = 0;
	p->tss.ebx = ebx;
	p->tss.ecx = ecx;
	p->tss.edx = edx;
	p->tss.edi = edi;
	p->tss.esi = esi;
	p->tss.ebp = ebp;
	p->tss.esp = esp;
	p->tss.cs = cs & 0x0000ffff;
	p->tss.ds = ds & 0x0000ffff;
	p->tss.es = es & 0x0000ffff;
	p->tss.fs = fs & 0x0000ffff;
	p->tss.gs = gs & 0x0000ffff;
	p->tss.ss = ss & 0x0000ffff;
	p->tss.ldt = TASK_LDT_SELECTOR(nr);
	p->tss.trace_bitmap = 0x80000000;
	p->tss.i387 = current_task->tss.i387;

	if(last_task_used_math == current_task){
		__asm__("clts; fnsave %0"::"m"(p->tss.i387));
	}
	set_tss_desc(&gdt[TASK_TSS_ENTRY(nr)], &(p->tss));
	set_ldt_desc(&gdt[TASK_LDT_ENTRY(nr)], &(p->ldt));

	p->state = TASK_RUNNING;
	task[nr] = p;
	return p->pid;
}

/*
 * 系统调用 sys_fork
 */
int sys_fork(long ebx, long ecx, long edx, long edi, long esi, long ebp, 
		long ds, long es, long fs, long gs, 
		long eip, long cs, long eflags, 
		long esp, long ss)
{
	int nr;
	printk("sys_fork\n\r");
	//获取空闲的任务号
	nr = find_empty_process();
	//若任务号小于0, 出错, 返回.
	if(nr<0) return nr;
	return copy_process(nr, ebx, ecx, edx, edi, esi, ebp,
			ds, es, fs, gs,
			eip, cs, eflags, esp, ss);	
}
